package org.springframework.beans.factory.support;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Method;

/**
 * 统一处理对Bean的销毁 不论是接口还是xml指定
 */
public class DisposableBeanAdapter implements DisposableBean {
    private final Object bean;
    private final String beanName;

    private final String destroyMethodName;

    public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {
        this.bean = bean;
        this.beanName = beanName;
        this.destroyMethodName = beanDefinition.getDestroyMethodName();
    }

    /**
     * 执行对bean的销毁 可以是实现了接口 也可以是指定了销毁方法
     *
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        //实现了DisposableBean接口，执行自己实现的销毁方法即可
        if (bean instanceof DisposableBean) {
            ((DisposableBean) bean).destroy();
        }


        //避免同时继承自DisposableBean，且自定义方法与DisposableBean方法同名，销毁方法执行两次的情况 不能一个接口的销毁方法执行两次
        //调用指定的销毁方法来销毁 如果即实现了接口 又指定了一个不同于接口的销毁方法 那么都会执行 接口的先执行
        if(StrUtil.isNotEmpty(destroyMethodName) &&
                !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))){

            //通过反射获取指定的销毁方法
            Method destroyMethod = ClassUtil.getPublicMethod(bean.getClass(), destroyMethodName);
            if(destroyMethod == null){
                throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
            }
            //执行指定的销毁方法
            destroyMethod.invoke(bean);
        }
    }
}
